/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package Control;

import Model.Attribute;
import Model.Concept;
import Model.Ontology;
import java.io.File;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.semanticweb.owlapi.apibinding.OWLManager;
import org.semanticweb.owlapi.model.OWLAxiom;
import org.semanticweb.owlapi.model.OWLClass;
import org.semanticweb.owlapi.model.OWLClassExpression;
import org.semanticweb.owlapi.model.OWLIndividual;
import org.semanticweb.owlapi.model.OWLObjectProperty;
import org.semanticweb.owlapi.model.OWLOntology;
import org.semanticweb.owlapi.model.OWLOntologyCreationException;
import org.semanticweb.owlapi.model.OWLOntologyManager;
import org.semanticweb.owlapi.util.AutoIRIMapper;

/**
 *
 * @author Toinamph
 */
public class OntologyController {

    private static Ontology ontology;

    /**
     * this method parse the ontology file *.owl into our model.
     *
     * @param url
     * @return Ontology object
     * @throws OWLOntologyCreationException
     */
    public static Ontology parseOWL(String url) throws OWLOntologyCreationException {
        ontology = new Ontology(); // our Ontology stucture
        ontology.setLink(url);
        ontology.setName(getOntologyName(url));

        System.out.println("Name of ONTOLOGY = " + ontology.getName());

        OWLOntologyManager m = OWLManager.createOWLOntologyManager();
        m.addIRIMapper(new AutoIRIMapper(new File("materializedOntologies"), true));
        OWLOntology owlOntology = m.loadOntologyFromOntologyDocument(new File(url));
        if (owlOntology.isEmpty()) {
            return ontology;
        }

        // We create a concept for an owl class
        for (OWLClass cls : owlOntology.getClassesInSignature()) {
            if (!cls.toString().contains("owl:Thing")) { // we don't deal with owl:Thing
                String conceptName = parseName(cls.toString());
                Concept concept = new Concept(conceptName);

                if (ontology.getConcepts().contains(concept)) {
                    /* if the concepts already exists in the ontology */
                    concept = ontology.getConcepts().get(ontology.getConcepts().indexOf(concept));
                } else {
                    // Add the concept to the ontology
                    ontology.addConcept(concept);
                }

                // Get the subclasses
                Set<OWLClassExpression> ssc = cls.getSubClasses(owlOntology);
                for (Iterator<OWLClassExpression> i = ssc.iterator(); i.hasNext();) {
                    OWLClassExpression sc = (OWLClassExpression) i.next();
                    String subConceptName = parseName(sc.toString());
                    Concept subConcept = new Concept(subConceptName);

                    if (ontology.getConcepts().contains(subConcept)) {
                        /* if the concepts already exists in the ontology */
                        subConcept = ontology.getConcepts().get(ontology.getConcepts().indexOf(subConcept));
                    } else {
                        ontology.addConcept(subConcept);
                    }
                    subConcept.addParent(concept);
                    concept.addChild(subConcept);
                    if (subConceptName.equals("Parent")) {
                        int stop = 1;
                    }
                }

                // The equivalent concepts appear as child concept with Protege, we will do the same
                Set<OWLClassExpression> eqClasses = cls.getEquivalentClasses(owlOntology);
                for (Iterator<OWLClassExpression> i = eqClasses.iterator(); i.hasNext();) {
                    OWLClassExpression eqClass = (OWLClassExpression) i.next();
                    String eqConceptName = parseName(eqClass.toString());

                    if (Character.isUpperCase(eqConceptName.charAt(0))) { // classes names start with an uppercase 

                        Concept eqConcept = new Concept(eqConceptName);

                        if (ontology.getConcepts().contains(eqConcept)) {
                            // if the concepts already exists in the ontology
                            eqConcept = ontology.getConcepts().get(ontology.getConcepts().indexOf(eqConcept));
                        } else {
                            ontology.addConcept(eqConcept);
                        }

                        /* we need to check that the equivalent class is not a subclass as well */
                        if (!concept.getChildren().contains(eqConcept) && !eqConcept.getChildren().contains(concept)) {
                            eqConcept.addChild(concept);
                            concept.addParent(eqConcept);
                        }
                    }
                }
            }
        }

        // Adding the attributes
        for (OWLAxiom a : owlOntology.getAxioms()) {
            System.out.println(a.toString());
            String s = a.toString();
            if (s.contains("PropertyDomain")) {
                ArrayList<String> res = parseDataPropertyDomain(s);
                String property_name = res.get(0); // name of the property
                String domain_name = res.get(1); // the domain of a property is the class to which this property applies
                System.out.println(property_name);
                System.out.println(domain_name);
                // we need to add the attribute for the class and all its descendants
                for (Concept c : ontology.getConcepts()) {
                    if (c.getName().equals(domain_name)) {
                        c.addAttributeRecursively(new Attribute(property_name));
                    }
                }
            }
        }


        return ontology;

    }

    public static void readOntology(String url) throws OWLOntologyCreationException {

        Ontology myOnt = new Ontology();

        OWLOntologyManager m = OWLManager.createOWLOntologyManager();
        m.addIRIMapper(new AutoIRIMapper(new File("materializedOntologies"), true));
        OWLOntology o = m.loadOntologyFromOntologyDocument(new File(url));
        if (o.isEmpty()) {
            System.out.println("Ontology is empty");
        } else {
            System.out.println("Ontology is not empty");
        }

        /*System.out.println("Displaying the individuals : ");
         for (OWLNamedIndividual ind : o.getIndividualsInSignature()) {
         System.out.println(ind.toString());
         } */

        System.out.println("Displaying the classes : ");
        for (OWLClass cls : o.getClassesInSignature()) {

            System.out.println("New class : " + cls);

            // Printing the individuals
            System.out.println("<Individuals>");
            Set<OWLIndividual> si = cls.getIndividuals(o);
            for (Iterator<OWLIndividual> i = si.iterator(); i.hasNext();) {
                OWLIndividual ind = (OWLIndividual) i.next();
                System.out.println(ind.toString());
            }
            System.out.println("</Individuals>");

            // Printing the subclasses
            System.out.println("<SubClasses>");
            Set<OWLClassExpression> ssc = cls.getSubClasses(o);
            for (Iterator<OWLClassExpression> i = ssc.iterator(); i.hasNext();) {
                OWLClassExpression sc = (OWLClassExpression) i.next();
                System.out.println(sc.toString());
            }
            System.out.println("</SubClasses>");

            // Printing the properties
            System.out.println("<properties>");
            Set<OWLObjectProperty> sop = cls.getObjectPropertiesInSignature();
            for (Iterator<OWLObjectProperty> i = sop.iterator(); i.hasNext();) {
                OWLObjectProperty op = (OWLObjectProperty) i.next();
                System.out.println(op.toString());
            }
            System.out.println("</properties>");

            System.out.println();
        }

        // Trying to display the tree!
        ArrayList<String> alreadyDisplayed = new ArrayList<String>() {
        };
        for (OWLClass cls : o.getClassesInSignature()) {
            displayClass(cls, 0, o, alreadyDisplayed);
        }
    }

    private static void displayClass(OWLClass cls, int level, OWLOntology o, ArrayList<String> alreadyDisplayed) {
        if (alreadyDisplayed.contains(cls.toString())) {
            return;
        }

        for (int i = 0; i < level; i++) {
            System.out.print("\t");
        }
        System.out.println(cls.toString());
        alreadyDisplayed.add(cls.toString());

        // Recursively call the method on the subclasses

        for (Iterator<OWLClassExpression> i = cls.getSubClasses(o).iterator(); i.hasNext();) {
            OWLClassExpression subClass = (OWLClassExpression) i.next();

            for (OWLClass currentClass : o.getClassesInSignature()) {
                // System.out.println(currentClass.toString());
                // System.out.println(subClass.toString());
                if (currentClass.toString().equals(subClass.toString())) {
                    displayClass(currentClass, level + 1, o, alreadyDisplayed);
                    break;
                }
            }
        }
    }

    /**
     * parse the whole address to get the real name of the concept example :
     * <http://www.co-ode.org/ontologies/pizza/pizza.owl#SpicyTopping> -->
     * SpicyTopping
     *
     * @param toString
     * @return
     */
    private static String parseName(String s) {
        /*String [] tab = s.split("#");
         String ret = null;
         if(tab.length > 1) {
         ret = tab[1];
         }
         else {
         ret = tab[0];
         }
         return ret;*/

        String ret = "";
        if (s.equals("owl:Thing")) {
            return s;
        }

        int index = 0;
        while (s.charAt(index) != '#') {
            index++;
        }
        index++;
        while (s.charAt(index) != '>') {
            ret += s.charAt(index);
            index++;
        }

        return ret;

    }

    /**
     * Parse the URL to return the ontology name
     *
     * @param url
     * @return
     */
    public static String getOntologyName(String url) {
        String ret = "";
        int index = url.length() - 1;
        while (index > 0 && url.charAt(index) != '\\' && url.charAt(index) != '/') {
            ret = url.charAt(index) + ret;
            index--;
        }
        if(ret.endsWith(".owl")) {
            ret = ret.substring(0, ret.length() - 4);
        }
        return ret;
    }

    /**
     * Returns a concept in the ontology
     *
     * @param search : the name of the concept to be searched for
     * @return the concept if it exists, null otherwise
     */
    public static Concept searchConcept(String search) {
        search = search.toUpperCase(); // we don't consider the case
        System.out.println(search);
        for (Concept c : ontology.getConcepts()) {
            String conceptName = c.getName().toUpperCase();
            if (conceptName.equals(search)) {
                return c;
            }
        }

        /* if we haven't found an exact match, we try to look for a concept whose name contains the search */
        for (Concept c : ontology.getConcepts()) {
            String conceptName = c.getName().toUpperCase();
            if (conceptName.contains(search)) {
                return c;
            }
        }
        return null;
    }

    /**
     *
     * @param search
     * @return all the concepts that match the search
     */
    public static List<Concept> advancedSearch(String search) {
        search = search.toUpperCase(); // we don't consider the case
        List<Concept> conceptsToReturn = new ArrayList<Concept>();
        for (Concept c : ontology.getConcepts()) {
            String conceptName = c.getName().toUpperCase();
            if (conceptName.contains(search)) {
                if (noAncestorInTheList(conceptsToReturn, c)) {
                    conceptsToReturn.add(c);
                    removeAllDescendants(conceptsToReturn, c);
                }
            }
        }
        return conceptsToReturn;
    }

    private static void removeAllDescendants(List<Concept> listConcepts, Concept c) {
        for (Concept son : c.getChildren()) {
            listConcepts.remove(son);
            removeAllDescendants(listConcepts, son);
        }
    }

    private static boolean noAncestorInTheList(List<Concept> conceptsToReturn, Concept c) {
        for (Concept parent : c.getParents()) {
            if (conceptsToReturn.contains(parent)) {
                return false;
            }
            return noAncestorInTheList(conceptsToReturn, parent);
        }
        return true;

    }

    public static Ontology getOntology() {
        return ontology;
    }

    private static ArrayList<String> parseDataPropertyDomain(String s) {
        String property_name = "";
        String domain_name = "";
        int index = 0;
        while (s.charAt(index) != '#') {
            index++;
        }
        index++;
        while (s.charAt(index) != '>') {
            property_name += s.charAt(index);
            index++;
        }
        while (s.charAt(index) != '#') {
            index++;
        }
        index++;
        while (s.charAt(index) != '>') {
            domain_name += s.charAt(index);
            index++;
        }

        ArrayList<String> ret = new ArrayList<>(2);
        ret.add(property_name);
        ret.add(domain_name);
        return ret;
    }
}
